Model III ROM Explained - Part 2
Page Customization
Navigation
Disassembly
Related Pages
... continuation
This page covers 1001H-2002H of the Model III ROM. The ONLY difference between the Model I v1.3 ROM and the Model III ROM in this entire range is located at 1B5DH-1B5FH. On the Model I, that address loaded HL with the contents of memory location 40A4H, the start of data. On the Model III, that adress is a GOSUB to unprotect the screen and load HL with the start of data.
There is no difference between the early Model III ROM and the later Model III ROM in this range. The Model 4 ROM is also identical to the Model III ROM in this range.
NOTE:
- The RST 10H routine loads the next character from the string pointed to by the HL register into the A-register and clears the CARRY FLAG if it is alphabetic, or sets it if is alphanumeric.
- Blanks and control codes 09H and 0BH are ignored causing the following character to be loaded and tested.
- The HL register will be incremented before loading any character therfore on the first call the HL register should contain the string address minus one.
- The string must be terminated by a byte of zeros.
1034 - LEVEL II BASIC MATH ROUTINE
This routine initializes the input buffer for an ASCII conversion. It is called by the FLOATING to ASCII Conversion Routine (at 0FBEH) and by the BINARY to ASCII Conversion Routine (at 0FAFH).
NOTE: 40D8H-40D9H holds the temporary storage location.
NOTE: 4130H-4149H holds Internal print buffer.
103D - LEVEL II BASIC MATH ROUTINE
This routine gets called by the FLOATING to ASCII Conversion Routine (0FBEH-0FC0H) if the value being converted is either Single Precision or Double Precision. This will print a single or double precision number in free format
NOTE: Results from a CP:
- Z: A and * are the same
- NZ: A and * are NOT the same
- C: A < *
- NC: A => *
NOTE: Results from a CP:
- Z: A and * are the same
- NZ: A and * are NOT the same
- C: A < *
- NC: A => *
NOTE: The RST 20H routine determines the type of the current value in REG 1 and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH).
The results are returned as follows:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
NOTE: 4130H-4149H holds Internal print buffer.
109A - Print a number in a fixed format
- If A=04H it sets the ZERO FLAG
- If A<04H then the CARRY FLAG will be set
- If A>=04H then the NO CARRY FLAG will be set
Alternative interpretation is Jump to 11A3H if the variable is a string; which will leave us now with only an integer value.
10A6 - This is where the PRINT USING routine will edit for INTEGER IN FIXED FORMAT-FIXED POINT NOTATION.
10BF - Finish up a fixed format number
NOTE: 40F3H-40F4H holds the temporary storage location.
NOTE:
- The RST 10H routine loads the next character from the string pointed to by the HL register into the A-register and clears the CARRY FLAG if it is alphabetic, or sets it if is alphanumeric.
- Blanks and control codes 09H and 0BH are ignored causing the following character to be loaded and tested.
- The HL register will be incremented before loading any character therfore on the first call the HL register should contain the string address minus one.
- The string must be terminated by a byte of zeros.
NOTE:
- The RST 10H routine loads the next character from the string pointed to by the HL register into the A-register and clears the CARRY FLAG if it is alphabetic, or sets it if is alphanumeric.
- Blanks and control codes 09H and 0BH are ignored causing the following character to be loaded and tested.
- The HL register will be incremented before loading any character therfore on the first call the HL register should contain the string address minus one.
- The string must be terminated by a byte of zeros.
1109 - This is where the PRINT USING routine will print a single or double in fixed format
1124H - Continuation point if the current value in REG 1 is single precision
NOTE: The routine at 0A0CH algebraically compares the single precision value in BC/DE to the single precision value REG 1.
The results are stored in A as follows:
- A=00H if REG 1 = BCDE
- A=01H if REG 1>BCDE; and
- A=FFH if REG 1<BCDE.
1157 - LEVEL II BASIC MATH ROUTINE
This routine will print a SINGLE PRECISION or DOUBLE PREVISION number that has fractional digits
117F - LEVEL II BASIC MATH ROUTINE - HERE TO PRINT A NUMBER WITHOUT INTEGER DIGITS
This routine will print a number without integer digits.
119A - LEVEL II BASIC MATH ROUTINE
This routine will print trailing zeroes.
NOTE: 40F3H-40F4H holds the temporary storage location.
11A3 - LEVEL II BASIC MATH ROUTINE
This routine will print an integer in fixed format/floating point notation.
11AA - LEVEL II BASIC MATH ROUTINE
This routine will print a SINGLE or DOUBLE PRECISION number in fixed format/floating point notation.
or
LD BC, 1E06H
11B0H - Continuation Routine (From 11AAH) if the current value in REG 1 is SINGLE precision.
1201 - Test the magnitude of SP and DP numbers, and clear the times the value was scaled
This routine will scale (normalize) the number in the accumulator so that all the digits are in the integer part (i.e., between 99,999 and 999,999). The signed base 10 exponent is returned in Register A. Registers D and E are unchanged.
NOTE: The RST 20H routine determines the type of the current value in REG 1 and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH).
The results are returned as follows:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
NOTE: Results from a CP:
- Z: A and * are the same
- NZ: A and * are NOT the same
- C: A < *
- NC: A => *
NOTE: 4127H-412EH holds REG 2.
NOTE: The RST 20H routine determines the type of the current value in REG 1 and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH).
The results are returned as follows:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
NOTE: The routine at 0A0CH algebraically compares the single precision value in BC/DE to the single precision value REG 1.
The results are stored in A as follows:
- A=00H if REG 1 = BCDE
- A=01H if REG 1>BCDE; and
- A=FFH if REG 1<BCDE.
Difference between M1 and M3 ROMs: This change first appeared in the "new" ROMs for the Model I. The order of two instructions (OR A and POP DE) have been reversed for no apparent reason.
124F - HERE TO SEE IF THE FAC IS SMALL ENOUGH YET
NOTE: The RST 20H routine determines the type of the current value in REG 1 and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH).
The results are returned as follows:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
NOTE: The routine at 0A0CH algebraically compares the single precision value in BC/DE to the single precision value REG 1.
The results are stored in A as follows:
- A=00H if REG 1 = BCDE
- A=01H if REG 1>BCDE; and
- A=FFH if REG 1<BCDE.
1269H - This routine puts leading zeroes into the input buffer
Here to put zeros in the buffer with commas or a decimal point in the middle. The count is in a, it can be zero, but the zero flag must be set. B the decimal point count and c the comma count are updated
127DH - Subroutine to set up BC for decimal point and comma counters.
On entry: Register D holds the number of digits to print, Register E holds the count of the times the value was scaled up or down.
NOTE: 40D8H-40D9H holds the temporary storage location.
1291H - Subroutine to count the leading digits before the decimal point
HERE TO PUT DECIMAL POINTS AND COMMAS IN THEIR CORRECT PLACES. THIS SUBROUTINE SHOULD BE CALLED BEFORE THE NEXT DIGIT IS PUT IN THE BUFFER. B=THE DECIMAL POINT COUNT, C=THE COMMA COUNT THE COUNTS TELL HOW MANY MORE DIGITS HAVE TO GO IN BEFORE THE COMMA OR DECIMAL POINT GO IN. THE COMMA OR DECIMAL POINT THEN GOES BEFORE THE LAST DIGIT IN THE COUNT. FOR EXAMPLE, IF THE DECIMAL POINT SHOULD COME AFTER THE FIRST DIGIT, THE DECIMAL POINT COUNT SHOULD BE 2.
NOTE: 40F3H-40F4H holds the temporary storage location.
129CH - Continuation Routine (from 1292H) if the decimal point position hasn't been reached
12A4 - Routine to Convert the INTEGER porton of a DOUBLE precision value to its ASCII equivalent.
HERE TO CONVERT A SNG OR DBL NUMBER THAT HAS BEEN NORMALIZED TO DECIMAL DIGITS. THE DECIMAL POINT COUNT AND COMMA COUNT ARE IN B AND C RESPECTIVELY. (HL) POINTS TO WHERE THE FIRST DIGIT WILL GO. THIS EXITS WITH A=0. (DE) IS LEFT UNALTERED.
NOTE: The RST 20H routine determines the type of the current value in REG 1 and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH).
The results are returned as follows:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
Note: 411DH-4124H holds REG l.
12EA - LEVEL II BASIC MATH ROUTINE
This routine is to convert a SINGLE precision value to an INTEGER which will be the decimal digits. Divide the integer equivalent by 100,000 and 10,000. Use the code at 1335H to convert the last 1000 to ASCII.
130AH - This routine divides the integer portion of the current value by 100,000 using compound subtraction. The quotient is kept in Register B as an ASCII value.
132FH - This routine will convert an INTEGER to ASCII
1335H - This routine will convert the last x (based on A) digits of an INTEGER to ASCII.
1348 - This loop divides the current value by a power of 10 starting at 10,000 and working down to 10. The remainder frome ach division is added to the division and the sum becomes the dividend for the next division until done. The quotient is +2FH (which is the ASCII equivalent of a quotient).
1364-136B - DOUBLE PRECISION CONSTANT STORAGE LOCATIONS
BYTE SAVING NOTE: Referencing 1380H, which is half-way through this double precision value of .5, results in a single precision value of 0.5.
BYTE SAVING NOTE: Referencing 1380H, which is half-way through this double precision value of .5, results in a single precision value of 0.5.
138C-13D1 - DOUBLE PRECISION INTEGER CONSTANT STORAGE LOCATION
13D2H-13D9H - SINGLE PRECISION INTEGER POWER OF 10 TABLE
13E2-13E6 - LEVEL II BASIC MATH ROUTINE
NOTE: 0982H is the address of the routine for conversion of floating point numbers from negative to positive.
13E7-13F1 - LEVEL II BASIC - "SQR(n)" Function
This routine computes the square root of any value in ACCumulator. It processes it by raising n to the power of 0.5. The root is left in ACCumulator as a single precision value. Single-precision values only should be used
13F2-1478H - LEVEL II BASIC X to the Y Power (X^Y) ROUTINE
A call to 13F2H raises the single precision value which has been saved to the STACK to the power specified in ACCumulator. The result will be returned in ACCumulator. The method of computation is e ** (y ln x).
NOTE: To use a ROM call to raise a single precision number (the base) to a single precision power (the exponent), store the base (single precision) in registers BCDE, and store the exponent (also single precision) in 4121H-4124H and then CALL 13F7H. The result (in single precision format) is in 4121H-4124H and in approximately 50 milliseconds.
/0 ERROR entry point.
NOTE: The routine at 0A0CH algebraically compares the single precision value in BC/DE to the single precision value REG 1.
The results are stored in A as follows:
- A=00H if REG 1 = BCDE
- A=01H if REG 1>BCDE; and
- A=FFH if REG 1<BCDE.
1439 - "EXP" routine. Single-precision only. (REG 1 = EXP(REG1)).
A call to 1439H raises E (natural base) to the value in REG 1 which must be a single precision value. The result will be returned in REG 1 as a single precision number.
NOTE: To use a ROM call to find EXP(X), where X is a single precision variable, store the value of X in 4121H-4124H and then CALL 1439H. The result (in single precision format) is in 4121H-4124Hin approximately 28 milliseconds. NOTE: A fatal error occurs if the result is as large as 2 to the power of 127.
1479-1499 - SINGLE PRECISION CONSTANT STORAGE LOCATION
This represents 1/6, -1/5, 1/4, -1/3, 1/2, -1, and 1
149A-14C8 - LEVEL II BASIC MATH ROUTINE - Polynomial Evaluator and Random Number Generator
This is a general purpose summation routine which computes the series SUM ((((x^2 * c0+c1)x^2 +c2)x^2 + ... cN)x for I=0 to N when entered at 149AH If entered at 14A9H the series changes to SUM ((((x*c0+c1)x*c2)x+c3)x+...cN. On entry, the x is held in BC/DE and HL points to a list containing the number of terms followed by the coefficients.
NOTE: 0C32H pops BC and DE and then JUMPs to JP 0847HJP FMULT.
POP DE
PUSH BC
14C9-1540 - LEVEL II BASIC "RND(n)". Generates a single-precisions random number between 0 and 1, or 1 and n depending on the parameter passed in REG 1
, The random value is returned in REG 1 as an integer with the mode flag set. The parameter passed will determine the range of the random number returned. A parameter of 0 will return an interger between 0 and 1. A parameter greater than 0 will have any fraction portion truncated and will cause a value between 1 and the integer portion of the parameter to be returned.
NOTE: To run a RND(J) function via a ROM call just The RND(J) function (with J a nonzero integer) load the value of J into the HL register pair. Then CALL 14CCH and then CALL 0A7FH. The result (in integer format) is in 4121H-4122H and in HL in approximately 5.7 milliseconds. The input variable J must have a value between 1 and 32767, inclusive, or an FC error will occur.
POP DE
14F0H - This routine calculates "RND(0)" Function
NOTE: To run a RND(0) function via a ROM call just CALL 14F0H. No input variable is necessary. The result (in single precision format) is in 4121H-4124H in approximately 2.4 milliseconds.
NOTE: 4090H holds the random number seed 2
NOTE: 40AAH-40ADH holds the random number seed.
NOTE: 40AAH-40ADH holds the random number seed.
NOTE: 4125H-4126H is used by floating point routines.
1541-1546 - LEVEL II BASIC - "COS()" Function
COS routine. Single-precision only.(REG 1 = COS(REG 1)).
A call to 1541H computes the cosine for an angle given in radians. The angle must be a floating point value in REG 1; the cosine will be returned in REG 1 as a floating point value.
NOTE: To use a ROM call to find COS(X), where X is a single precision variable (in radians), store the value of X in 4121H-4124H and then CALL 1541H. The result (in single precision format) is in 4121H-4124Hin approximately 25 milliseconds.
1547-158A - LEVEL II BASIC - "SIN()" Function
SIN(n) routine. Single-precision only.(REG 1 = SIN(REG 1)).
A call to 1549H returns the sine as a single precision value in REG 1. The sine must be given in radians in REG 1.
The actual calculation routine is:
Assume X <= 360 degrees.
Recompute x as x=x/360 so that x=< 1.
If x <= 90 degrees go to step 7.
If x <= 180 degrees then x=0.5-x and then go to step 7.
If x <= 270 degrees then x=0.5-x.
Recompute x as x=x-1.0.
Compute SIN using the power series.
NOTE: To use a ROM call to find SIN(X), where X is a single precision variable, store the value of X in 4121H-4124H and then CALL 1547H. The result (in single precision format) is in 4121H-4124Hin approximately 25 milliseconds. NOTE: The argument (X) must be in radians.
POP DE
POP DE
158BH-15A7H - SINGLE PRECISION CONSTANT STORAGE LOCATION - Approximations for SIN and COS
15A8-15BC - LEVEL II BASIC - "TAN(n)" Function
Single-precision only.(REG 1 = TAN(REG 1)).
A call to 15A8H computes the tangent of an angle in radians. The angle must be specified as a single precision value in REG 1. The tangent will be left in REG 1.
Uses the fact that TAN(x) = SIN(x) / COS(x)
NOTE: To use a ROM call to find TAN(X), where X is a single precision variable (in radians), store the value of X in 4121H-4124H and then CALL 15A8H. The result (in single precision format) is in 4121H-4124Hin approximately 54 milliseconds. NOTE: A fatal error occurs if the result is as large as 2 to the power of 127, which will be the case if the value of X is sufficiently close to any odd multiple of pi/2 radians.
POP HL
15BD-15E2 - LEVEL II BASIC ATN ROUTINE "ATN(n)" routine
Single-precision only.(REG 1 = ATN(REG 1)).
A call to 15BD returns the angle in radians, for the floating point tangent value in REG 1. The angle will be left as a single precision value in REG 1.
The method of computation used in this routine is:
Test the sign of the tangent to see if a negative angle is in the 2nd or 4th quadrant. Set the flag to force the result to positive on exit. If the value is negative, invert the sign.
Test magnitude of tangent. If it is < 1 go to step 3. Otherwise, compute its reciprocal and put the return address on the stack that will calculate pi/2 - series value.
Evaluate the series: (((x^2*c0+c1) x^2+c2) ... c8)x
If the flag from step 1 is not set, then invert the sign of the series result.
If the original value is < 1 then return to the caller. Otherwise, compute pi/2-value from step 4 and then return.
NOTE: To use a ROM call to find ATN(X), where X is a single precision variable, store the value of X in 4121H-4124H and then CALL 15BDH. The result (in single precision format, in radians) is in 4121H-4124Hin approximately 27 milliseconds.
- If A="1" it sets the ZERO FLAG.
- If A<"1" then the CARRY FLAG will be set
- If A>="1" then the NO CARRY FLAG will be set.
15E3-1607 - SINGLE PRECISION CONSTANTS STORAGE LOCATION for the Constants for ATN
How Tokenization Works for 1608H–1906H
How Tokenization Works for 1608H–1906H
This 766-byte block is the entire BASIC token machinery — encoder, decoder, and all the dispatch tables. It's built around one elegant trick: the position of a keyword in RESLST is its token value, offset by 0x80. Every other table in the block is indexed by that same offset. Here's how each section pulls its weight.
The Unifying Idea
In the original Gates source you'll see an assembler symbol Q that starts implicitly at 127 and gets incremented once for every keyword string. The symbols $END, $FOR, $NEXT, $DATA, $INPUT, $GOTO, $IF, $GOSUB, $REM, $ELSE, $ERROR, $PRINT, $NEW, TABTK, $TO, FNTK, LSTOPK, ONEFUN, LASNUM, etc. are all just snapshots of Q taken at the right moment. The runtime never stores any of them — they're assemble-time constants used in the surrounding BASIC/CMD code (the bulk of which lives outside this range) to compare a token against a known value, e.g. CPI $REM, CPI $GOTO, CPI THENTK. So the positions in RESLST and the values used elsewhere in the ROM are bound together at build time. You can't reorder RESLST without recompiling the whole BASIC, and you can't change the token values without breaking cassette compatibility — which is exactly what the source comment at line 6085 warns about.
1608H–164DH — FUNDSP (Function Dispatch Table)
This is a flat array of 2-byte little-endian addresses, one per built-in function. Order matches the function portion of RESLST exactly (SGN, INT, ABS, FRE, INP, POS, SQR, RND, LOG, EXP, COS, SIN, TAN, ATN, PEEK, CVI, CVS, CVD, EOF, LOC, LOF, MKI$, MKS$, MKD$, CINT, CSNG, CDBL, FIX, LEN, STR$, VAL, ASC, CHR$, LEFT$, RIGHT$, MID$).
The dispatch code is at FINGO:
The caller computes the offset as (token − ONEFUN) × 2 where ONEFUN = $D7 (SGN). So when the parser sees token $E0 (LOG), it produces offset (0xE0 − 0xD7) × 2 = 0x12, looks up the word at 1608H + 0x12 = 161AH, and finds 1439H — the LOG routine. That's why the table starts at 1608H: it's exactly aligned so the math works out. Functions like LEFT$ ($F8) and RIGHT$ ($F9) which take more than one argument are placed at the end so the type-coercion code in ISFUN can short-circuit them by testing CPI BOTCON and CPI TOPCON.
164EH–164FH — Two-Byte Trailing Bytes
The bytes 164E 91 and 164F 2A aren't really independent data. They're the trailing two bytes of MID$'s pointer (DEFW 2A91H at 164C). The .htm just labels them as alignment because the next section RESLST begins at 1650H. There's no actual padding here — it's just the disassembler showing the same bytes split visually across two lines.
1650H–1822H — RESLST: The Encoder Dictionary
This is where the actual tokenization happens and it is written in not-really-translatable 8080 code. Each keyword is stored in DCI format (Define Compressed Immediate) — but the DCI macro has a quirk that's important to understand:
QRDTYU is 128 for the first character and 0 for the rest. So DCI sets the high bit on the first character of each keyword, not the last. Looking at the bytes at 16D5 (RESUME): D2 45 53 55 4D 45 — that's ('R'|0x80) E S U M E. This is what the searcher in CRUNCH uses to find keyword boundaries — by scanning forward looking for the next byte with bit 7 set.
Some entries aren't DCI but raw DB. DCI items are handled by a macro, above. DB's are back to normal encoding and processing. This is why TAB( is in a different section than all the others!
Why is TAB different? The inclusion of the "(" in the command, which is actually "TAB(" and not "TAB" would have caused major issues if the macro applied, so "TAB(" was hard coded using DB's. More specifically, in Macro-80, parentheses inside macro arguments get treated as grouping characters by the IRPC iterator, and the comma-or-paren parsing rules around '&X1' substitution don't reliably handle ( as a literal character. So Gates dropped down to raw DB bytes.
- 1752 D4 41 42 28 — 'T'|0x80, 'A', 'B', '(' for the TAB( token. Encoded literally because the ( would confuse DCI's character iteration.
- 1798 AB — single byte '+'|0x80 for the + token. Operators are one-character DCI strings, which collapses to a single high-bit byte.
- 1799 AD, 179A AA, 179B AF — −, *, / likewise.
- 179C DB — exponent ^ token. Encoded as DB (200O+133O) = 0x80 + 0x5B = 0xDB. The ^ ASCII code is 0x5E, but Gates used 0x5B (which is [) — probably because early TRS-80 keyboards typed [ for ^ originally, and the source just hard-coded the displayed character.
- 17A3 BE, 17A4 BD, 17A6 BC — >, <, = direct DBs (operators stored out of natural order; see source lines 6240–6247).
- 1821 A7 — single-quote token 0x80 | 0x27, written as DB 247O in the source.
- 1822 80 — the terminator. The CRUNCH loop returns when it hits a byte whose low 7 bits are zero (ANI 127 / RZ).
The encoder is MUSTCR. It walks RESLST looking for high-bit-set bytes (the start-of-keyword markers), increments B (the running token counter, initialized to 127 so the first INR makes it 128 = $80), compares the rest of the keyword against the input, and on match returns the token value in B. This is also why smaller keywords must come after longer ones that contain them — the source comment explicitly calls out the trap with IF/FOR and INP/INPUT. Gates handles this by ordering: INPUT (token $89) is in the command block which appears in RESLST before INP (token $DB) which is in the function block. So the scan finds INPUT first.
There are two delicate details:
- The CRUNCH loop has a special carve-out for GOTO — it allows spaces inside GOTO so GO TO 100 works.
- After encoding ELSE, the encoder inserts a hidden : colon byte in front of it, so the line parser treats ELSE as a fresh statement boundary even when it appears mid-line. The same colon-prefix trick is used for ' (single quote = REM).
1823H–189AH — STMDSP: Statement Dispatch Table
Another flat array of 2-byte addresses, this time for the statement portion of RESLST only (END through NEW, plus the disk-extension statements through SCRATH which is the NEW handler). Indexed exactly like FUNDSP. The dispatch code is at GONE2:
Notice the IOGOR:: label at 184D (where ELSE would go). That's a "global" label (::) used elsewhere in the ROM — the ELSE, TRON, TROFF entries are reached by a different path because ELSE is already handled by the hidden-colon trick.
189BH–18AFH — OPTAB: Operator Table
Seven 3-byte entries: precedence_byte, address_low, address_high. The operators here are +, −, *, /, ^, AND, OR in the same order they appear at tokens $CD–$D3 in RESLST (PLUSTK through OR).
The source uses an ADRP macro that has two definitions — one that emits DW X and one that emits nothing. Depending on which is active when the assembler processes OPTAB, you get either precedence-only entries or full 3-byte entries. The Model III ROM build has the address-emitting version active, so each entry is precedence + address = 3 bytes, totaling 7 entries × 3 = 21 bytes occupying 189BH–18AFH.
The dispatch code is in FRMEVL / LOPREL / ENDREL. The parser sees an operator character (already tokenized to $CD..$D3 for +, −, *, /, ^, AND, OR), subtracts PLUSTK ($CD) to get 0..6, multiplies by 3, indexes into OPTAB:
Higher precedence wins on the right side; the address two bytes after the precedence byte is what gets called. The precedence values define the entire arithmetic order:
| Operator | Precedence | Routine |
|---|---|---|
| ^ | 127 | FPWRT |
| * / | 124 | FMULTT / FDIVT |
| + − | 121 | FADDT / FSUBT |
| AND | 80 | AND |
| OR | 70 | OR |
Comparison operators (<, =, >) are handled separately via the LOPREL bit-mask path because they can be combined (<=, >=, <>).
18B0H–18B9H — FRCTBL: Type Coercion Table
Five DW entries, indexed by valtype AND 7 where valtype is 8 (double), 4 (single), 3 (string), 2 (integer):
| Index | Address | Routine | Purpose |
|---|---|---|---|
| 0 | 18B0 | FRCDBL | Coerce to double |
| 1 | 18B2 | DS 2 | (unused) |
| 2 | 18B4 | FRCINT | Coerce to integer |
| 3 | 18B6 | CHKSTR | Type-check string |
| 4 | 18B8 | FRCSNG | Coerce to single |
The dispatch is DOCNVF: ANI 7 / LXI H,FRCTBL / DAD B / CALL DISPAT. This is called by the assignment code (LET, etc.) to make the right-hand side conform to the variable's declared type.
18BAH–18D7H — DBLDSP / SNGDSP / INTDSP: Arithmetic Dispatch Tables
Three parallel 5-entry tables (10 bytes each), all in the same operator order: ADD, SUB, MULT, DIV, COMP. After the formula evaluator has fully resolved both operands to the same type, APPLOP picks the right table and indexes by operator number:
OPCNT ((($-DBLDSP)/2)-1 = 4) is computed at assembly time and used elsewhere for range checks. Note that ^ and AND/OR aren't in these tables — exponentiation always coerces to single (handled with CPI 127 / JZ EXPSTK), and the logical operators always coerce to integer (CPI 81 / JC ANDORD).
18D8H–1905H — ERRTAB: Error Message Table
Each entry is a 2-byte ASCII error code (DCE macro outputs both characters as plain ASCII). The DCL macro that would emit a long error message is empty in the ROM build:
So the ROM only carries the short codes (NF, SN, RG, OD, FC, OV, OM, UL, BS, DD, /0, ID, TM, OS, LS, ST, CN, NR, RW, UE, MO, FD, L3). Indices ERRNF=0, ERRSN=2, ERRRG=4, … (the symbols are computed via Q SET Q+2 in DCE, starting from Q = −2 so the first DCE makes ERRNF = 0).
The error printer fragment:
prints ?XX ERROR where XX is the two-letter code. ERRNAV = NONDSK = the index of L3 = the last non-disk error; LSTERR = Q+1 is used for range checks elsewhere.
How It All Fits Together
The whole tokenizer/interpreter loop, in one sentence: text comes in → CRUNCH walks RESLST and replaces matched words with their position-as-byte (high-bit set) → the executor reads a token, subtracts $80 and dispatches via STMDSP for statements, FUNDSP for functions, OPTAB for operators in expressions, with FRCTBL / DBLDSP / SNGDSP / INTDSP handling type-aware arithmetic and ERRTAB providing the error vocabulary. Every dispatch is a 1-byte index into an aligned table — fast, compact, and forever locked into that exact byte ordering by the cassette format.
1608H–161FH — FUNDSP: Function Dispatch Table (Pointers to Built-in Function Routines)
Each entry is a 2-byte little-endian pointer (DW) to the implementation routine for a built-in numeric function. Tokens $D7–$FA map into this table. The disassembler misreads these as Z80 opcodes; they are pure data.
164EH–164FH — Alignment / Gap Before Reserved Word List
Two bytes of padding before RESLST begins at 1650H.
1650H–181FH — RESLST: Compressed Reserved Word (Keyword) List
Each keyword is stored in DCI (Define Compressed Immediate) format: all characters in ASCII, but the final character has bit 7 set (|0x80). Token values start at $80 and increment. Command tokens first, then operators and function-name tokens.
Statement / Command Tokens ($80–$9E)
End-of-Command Marker + "TAB(" pseudo-token
Secondary Keyword / Operator / Function Tokens ($BD–$FF area)
1823H–188BH — STMDSP: Statement Dispatch Table
Table of 2-byte pointers (DW) to statement handler routines. Indexed by statement token ($80=END, $81=FOR, …). Order must match RESLST exactly. The disassembler misreads these as Z80 instructions — they are all data.
189AH–18A0H — OPTAB: Operator Precedence Table
Each entry was originally specified as a precedence byte followed by a 2-byte pointer (via the ADRP macro) to the evaluation routine. However, in this ROM build the active ADRP macro definition is empty — so only the precedence bytes appear in the ROM. The actual operator dispatch is done by the formula evaluator using DBLDSP/SNGDSP/INTDSP tables instead. Order matches the operator tokens $CD–$D3: +, −, *, /, ^, AND, OR.
18A1H–18AAH — FRCTBL: Type Coercion / Force Table
Used by assignment code to force the right-hand value to the type of the variable being assigned. Five 2-byte pointers indexed by valtype AND 7 (where valtype is 8=double, 4=single, 3=string, 2=integer). Slot 1 is unused.
18ABH–18C8H — DBLDSP / SNGDSP / INTDSP: Arithmetic Operator Dispatch Tables
Three parallel tables of operator routines for double precision, single precision, and integer types. Used by APPLOP after type matching is done. OPCNT is computed from DBLDSP size at assembly time.
DBLDSP — Double Precision Routines
SNGDSP — Single Precision Routines
INTDSP — Integer Routines
18C7H–18F4H — ERRTAB: Error Message Table
Each error entry is a 2-byte short code (DCE = define compressed error, stored as two ASCII chars) followed by a DCL (long message string, not stored in ROM in this build — DCL expands to nothing). The Q counter tracks the error number offset. Error numbers are used by ERR and ERL. Note: byte address 18C7 also serves as the ICOMP slot in INTDSP — the bytes do double duty in this build.
1608-18C8 - LIST OF BASIC RESERVED WORDS, TOKENS, AND ENTRY LOCATIONS AS FOLLOWS:
The original ROM source code makes an interesting note about the order of these reserved words. Some reserved words are contained in other reserved words, which will cause a problem. They given examples of:
- IF J=F OR T=5 will process a FOR
- INP is part of INPUT
- IF T OR Q THEN will process a TO
| Word | Token | Address | Word | Token | Address |
|---|---|---|---|---|---|
| ABS | D9 | 0977 | AND | D2 | 25FD |
| ASC | F6 | 2A0F | ATN | E4 | 15BD |
| AUTO | B7 | 2008 | CDBL | F1 | 0ADB |
| CHR$( | F7 | 2A1F | CINT | EF | 0A7F |
| CLEAR | B8 | 1E7A | CLOAD | B9 | 2C1F |
| CLOSE | A6 | 4185 | CLS | 84 | 01C9 |
| CMD | 85 | 4173 | CONT | B3 | 1DE4 |
| COS | El | 1541 | CSAVE | BA | 2BF5 |
| CSNG | F0 | 0ABl | CVD | E8 | 415E |
| CVI | E6 | 4152 | CVS | E7 | 4158 |
| DATA | 88 | 1F05 | DEF | DD | 415B |
| DEFDBL | 9B | 1E09 | DEFINT | 99 | 1E03 |
| DEFSNG | 9A | 1E06 | DEFSTR | 98 | 1E00 |
| DELETE | B6 | 2BC6 | DIM | 8A | 2608 |
| EDIT | 9D | 2E60 | ELSE | 95 | 1F07 |
| END | 80 | 1DAE | EOF | E9 | 4161 |
| ERL | C2 | 24DD | ERR | C3 | 24CF |
| ERROR | 9E | 1FF4 | EXP | E0 | 1439 |
| FIELD | A3 | 417C | FIX | F2 | 0B26 |
| FN | BE | 4155 | FOR | 81 | 1CA1 |
| FRE | DA | 27D4 | GET | A4 | 4174 |
| GOSUB | 91 | 1EB1 | GOTO | 5D | 1EC2 |
| IF | 8F | 2039 | INKEY$ | C9 | 019D |
| INP | DB | 2AEF | INPUT | 89 | 219A |
| INSTR | C5 | 419D | INT | D8 | 0B37 |
| KILL | AA | 4191 | LEFT$ | F8 | 2A61 |
| LEN | F3 | 2A03 | LET | 8C | 1F21 |
| LINE | 9C | 41A3 | LIST | B4 | 2B2E |
| LLIST | B5 | 2B29 | LOAD | A7 | 4188 |
| LOC | EA | 4164 | LOF | EB | 4167 |
| LOG | DF | 0809 | LPRINT | AF | 2067 |
| LSET | AB | 4197 | MEM | C8 | 27C9 |
| MERGE | A8 | 418B | MID$ | FA | 2A9A |
| MKD$ | EE | 4170 | NAME | A9 | 418E |
| NEW | BB | 1B49H | NEXT | 87 | 22B6 |
| NOT | CB | 25C4 | ON | A1 | 1FC6 |
| OPEN | A2 | 4179 | OR | D3 | 25F7 |
| OUT | AO | 2AFB | PEEK | E5 | 2CAA |
| POINT | C6 | 0132 | POKE | B1 | 2CB1 |
| POS | DC | 27F5 | B2 | 206F | |
| PUT | A5 | 4182 | RANDOM | 86 | 01D3 |
| READ | 8B | 21EF | REM | 93 | 1F07 |
| RESET | 82 | 0138 | RESTORE | 90 | 1D91 |
| RESUME | 9F | 1FAFH | RETURN | 92 | 1EDEH |
| RIGHT$ | F9 | 2A91 | RND | DE | 14C9 |
| RSET | AC | 419A | RUN | 8E | 1EA3 |
| SAVE | AD | 41A0 | SET | 83 | 0135 |
| SGN | D7 | 098A | SIN | E2 | 1547 |
| SQR | CD | 13E7 | STEP | cc | 2B01 |
| STOP | 94 | 1DA9 | STR$ | F4 | 2836 |
| STRING$ | C4 | 2A2F | SYSTEM | AE | 02B2 |
| TAB( | BC | 2137 | TAN | E3 | 15A8 |
| THEN | CA | TIME$ | C7 | 4176 | |
| TO | BD | TROFF | 97 | 1DF8 | |
| TRON | 96 | 1DF8 | USING | BF | 2CBD |
| USR | C1 | 27FE | VAL | FF | 2AC5 |
| VARPTR | C0 | 24EB | + | CD | 249F |
| - | CE | 2532 | * | CF | |
| / | D0 | ? | D1 | ||
| > | D4 | = | D5 | ||
| < | D6 | & | 26 | ||
| ' | FB | 3A93 |
18C9-18F6 - STORAGE LOCATION FOR LEVEL II BASIC ERROR MESSAGES - "ERRTAB"
WHEN AN ERROR CONDITION IS DETECTED [E] MUST BE SET UP TO INDICATE WHICH ERROR MESSAGE IS APPROPRIATE AND A BRANCH MUST BE MADE TO ERROR. THE STACK WILL BE RESET AND ALL PROGRAM CONTEXT WILL BE LOST. VARIABLES VALUES AND THE ACTUAL PROGRAM REMAIN INTACT. ONLY THE VALUE OF [E] IS IMPORTANT WHEN THE BRANCH IS MADE TO ERROR. [E] IS USED AS AN INDEX INTO ERRTAB WHICH GIVES THE TWO CHARACTER ERROR MESSAGE THAT WILL BE PRINTED ON THE USER'S TERMINAL.
18F7-1904 - STORAGE LOCATION FOR THE SINGLE PRECISION DIVISION ROUTINE
This code is moved from 18F7-191DH to 4080H-40A5H during non-disk initial setup.
1905-191C - STORAGE LOCATION FOR VALUES PLACED IN RAM UPON INITIALIZATION.
This code is moved to 408E during non-disk initial setup.
191DH-1935H - MESSAGE STORAGE LOCATION
1936-1954 - SCAN STACK ROUTINE
This routine is called with DE as the address of the NEXT index. It scans the stack backwards looking for a FOR push. If one is found, it gets the address of the index and compares with the DE that was in place when this routine was called. If it is equal, then it exits with A=0 and HL=Address of the variable. If it is not equal it will keep scanning until no FOR push is found and then exit with A<>0.
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
1955-1962 - DATA MOVEMENT ROUTINE
This routine moves a variable into another area specified by the caller. On entry BC is set as the end address of the list to move (which is the upper limit); DE is set as the start address of the list to move; and HL is the end of the area to move it to.
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
1963-197D - MEMORY CHECK ROUTINE - Computes the amount of space between HL and the end of memory at FFC6H
This routine is used to make sure a certain number of locations remain available for the stack.
This routine must be called by any routine which puts an arbitrary amount of stuff on the stack (i.e. any recursive routine like FRMEVL) it is also called by routines such as "GOSUB" and "FOR" which make permanent entries on the stack
Routines which merely use and free up the guaranteed NUMLEV stack locations need not call this
NOTE: 40FDH-40FEH holds the pointer to the starting address of free memory.
197A-197B - OM ERROR entry point
197E-1AF7 - LEVEL II BASIC COMMAND MODE
NOTE: 40A2H-40A3H holds the current BASIC line number.
NOTE: 40F2H holds the error flag.
NOTE: 40DAH-40DBH holds DATA line number.
NOTE: 40A2H-40A3H holds the current BASIC line number.
SN ERROR entry point.
NOTE: 40A2H-40A3H holds the current BASIC line number.
NOTE: 40EAH-40EBH holds the line number with error.
NOTE: 40ECH-40EDH holds EDIT line number.
NOTE: 40E8H-40E9H holds Stack pointer pointer.
NOTE: 409AH holds the RESUME flag.
NOTE: 40E6H-40E7H holds the temporary storage location.
NOTE: 40EEH-40EFH is used by RESUME.
NOTE: 40EAH-40EBH holds the line number with error.
AND L
NOTE: 40F5H-40F6H holds the last line number executed.
NOTE: 40F7H-40F8H holds the last byte executed.
NOTE: 40F0H-40F1H holds the ON ERROR adress.
NOTE: 40F2H holds the error flag.
NOTE: 41A6H-41E4H holds DOS links.
In NEWDOS 2.1, this would be a call to load a DISK BASIC error, with Register E holding the error number.
NOTE:
- The RST 10H routine loads the next character from the string pointed to by the HL register into the A-register and clears the CARRY FLAG if it is alphabetic, or sets it if is alphanumeric.
- Blanks and control codes 09H and 0BH are ignored causing the following character to be loaded and tested.
- The HL register will be incremented before loading any character therfore on the first call the HL register should contain the string address minus one.
- The string must be terminated by a byte of zeros.
NOTE: 40EAH-40EBH holds the line number with error.
NOTE:
- The routine at 28A7 displays the message pointed to by HL on current system output device (usually video).
- The string to be displayed must be terminated by a byte of machine zeros or a carriage return code 0D.
- If terminated with a carriage return, control is returned to the caller after taking the DOS exit at 41D0H (JP 5B99H).
This basically reserves the line number 65534 as a trigger for the next few steps.
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
1A19 - "$READY" - Jump to Model II BASIC "READY"
To exit from a machine-language program into BASIC's immediate mode, jump (not call) to $READY
Re-entry into BASIC command mode entry point. (see 6CCH also).
In NEWDOS 2.1, this is the start of BASIC just before BASIC shows the READY prompt.
NOTE:
- The routine at 28A7 displays the message pointed to by HL on current system output device (usually video).
- The string to be displayed must be terminated by a byte of machine zeros or a carriage return code 0D.
- If terminated with a carriage return, control is returned to the caller after taking the DOS exit at 41D0H (JP 5B99H).
NOTE: 409AH holds the RESUME flag.
1A33 - Many routines jump here as the start of the Level II BASIC interpreter
If this is a pass through from an error, it causes INIT to be restarted.
If the jump here was from an AUTO call, (40E4H) will have the increment number, (40E1H) will be 0 if no AUTO and non-zero if AUTO, and (40E2H) will have the starting line number.
NOTE: FFFFH is the command mode line number.
NOTE: 40A2H-40A3H holds the current BASIC line number.
NOTE: 40E2H-40E3H holds Current BASIC line number.
NOTE: 40E1H will hold 0 if no AUTO and non-zero if AUTO.
If we are here, then the BREAK key was not pressed.
NOTE: 40E4H-40E5H holds the AUTO increment number.
There is an explanation at 1E5AH as to why 65529 is the highest possible line number (vs 65535 which would make more sense).
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
NOTE: 40E2H-40E3H holds Current BASIC line number.
NOTE:
- The RST 10H routine loads the next character from the string pointed to by the HL register into the A-register and clears the CARRY FLAG if it is alphabetic, or sets it if is alphanumeric.
- Blanks and control codes 09H and 0BH are ignored causing the following character to be loaded and tested.
- The HL register will be incremented before loading any character therfore on the first call the HL register should contain the string address minus one.
- The string must be terminated by a byte of zeros.
NOTE: The routine at 1E5A converts the ASCII string pointed to by HL to an integer deposited into DE. If the routine finds a non-numerica character, the conversion is stopped.
1A8BH - This little looping routine is to clear any trailing blanks between the line number and the statement.
NOTE: 40E6H-40E7H holds the temporary storage location.
In NEWDOS 2.1, this is the input scanner after tokenizing the current statement.
NOTE: 40DDH holds INPUT flag.
NOTE:
- The RST 10H routine loads the next character from the string pointed to by the HL register into the A-register and clears the CARRY FLAG if it is alphabetic, or sets it if is alphanumeric.
- Blanks and control codes 09H and 0BH are ignored causing the following character to be loaded and tested.
- The HL register will be incremented before loading any character therfore on the first call the HL register should contain the string address minus one.
- The string must be terminated by a byte of zeros.
NOTE: 40ECH-40EDH holds EDIT line number.
- Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
- Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
Note: 40A7H-40A8H holds Input Buffer pointer.
In NEWDOS 2.1, this is the input scanner after updating the PST (Program Statement Table).
In NEWDOS 2.1, this is the input scanner after reinitializing BASIC
1AF8-1B0F - LINE POINTERS ROUTINE
This routine fixes the line pointers in a BASIC program. This is useful, for instance for a renumber program which has to move BASIC program lines from one location in memory to an other, which means that the line pointers would no longer be valid. This routine will fix them. Registers A, HL and DE are used.
NOTE: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST).
1B10-1B48 - EVALUATE LINE NUMBERS
This is called by LIST and DELETE. It converts the starting and ending linbers (X-Y) to binary and saves the ending line number on the stack. Then the code locates the program table address for the starting line. The routine leaves the address of the starting line in BC and the ending line number in the stack.
1B2C - This routine searches a BASIC program for a BASIC line with a line number matching the value in the DE register pair.
To use this routine, the required line number must be placed in the DE register pair. When a match is found, this routine sets the CARRY FLAG; the BC register pair points to the start of the required line, and the HL register points to the start of the next line. HL, AF and BC are used.
This is the the SEARCH FOR LINE NUMBER routine at 1B2C, which searches the Program Statement Table (PST) for a BASIC statement with the line number specified in the DE register pair. All registers are used. The exit conditions are:
C/Z=Line Found and BC is the starting address of the line in the PST and HL is the address following;
NC/Z=Line not found or too large and HL/BC will have the address of the next available PST location; and
NC/NZ=Line not found and BC=Address of the first line number greater than the one specified and HL will be the address of the following line.
Note: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST).
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
If we are here, then we have no match.
1B49-1B5C - LEVEL II BASIC "NEW" ROUTINE
- Note: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST).
- Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
1B5D-1BB2 - LEVEL II BASIC "RUN" ROUTINE
Differences between M1 and M3 ROMs: The locations 1B5DH - 1B5FH are part of the RUN, NEW, EDIT, etc. commands; in the Model I these three bytes contain a LD HL,(40A4H) instruction (resets HL to point to the start of the BASIC program). In the Model III, this has been replaced by a CALL 046BH instruction, which unprotects the video display in addition to resetting HL to the start of the BASIC program.
- This routine does a lot of variable resets and other things that are common to NEW as well, so NEW just does the special NEW stuff and than passes right through to here to reset the rest.
- To use a ROM call to RUN a BASIC program, starting with its first line, execute the following instructions:
LD HL,1D1EH
PUSH HL
JP 1B5DH
The ability to RUN a BASIC program from an assembly language program is valuable for linking the two programs.
CLEARC is a subroutine which initializes the variable and array space by reseting ARYTAB [the end of simple variable space] and STREND [the end of array storage]. It falls into STKINI which resets the stack. [h,l] is preserved.
NOTE: 40DFH-40E0H is used by DOS.
NOTE: 4101H-411AH holds Variable Declaration Table.
NOTE: 40F2H holds the error flag.
NOTE: 40F0H-40F1H is used by ON ERROR.
NOTE: 40F7H-40F8H holds the last byte executed.
NOTE: 40B1H-40B2H holds MEMORY SIZE? pointer.
NOTE: 40D6H-40D7H holds Next available location in string space pointer.
- Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
- Note: 40FBH-40FCH holds the starting address of the BASIC array variable storage area.
NOTE: 40FDH-40FEH holds the pointer to the starting address of free memory.
In NEWDOS 2.1 this initializes BASIC for a new routine.
1B8FH - Inside the "RUN" routine - Initialize the Level II BASIC variables and pointers
STKINI RESETS THE STACK POINTER ELIMINATING GOSUB & FOR CONTEXT. STRING TEMPORARIES ARE FREED UP, SUBFLG IS RESET, CONTINUING IS DISALLOWED, AND A DUMMY ENTRY IS PUT ON THE STACK. THIS IS SO FNDFOR WILL ALWAYS FIND A NON-"FOR" ENTRY AT THE BOTTOM OF THE STACK. [A]=0 AND [D,E] IS PRESERVED.
Note: 40A0H-40A1H holds the start of string space pointer.
Note: 40E8H-40E9H holds Stack pointer pointer.
NOTE: 40B5H-40D2H holds Temporary string work area.
NOTE: 40B3H-40B4H holds Next available location in the temporary string work area pointer.
NOTE: 40DCH holds FOR flag.
NOTE: 40DFH-40E0H holds Used by DOS.
1BB3-1BBF - KEYBOARD INPUT ROUTINE
This is the last of the general purpose input routines. This routine functions identically to the 361H routine with the exception that it prints a "?" on the screen (like INPUT does with BASIC) before allowing input from the keyboard.
To use a ROM call to display "?" on the video screen at the current cursor position (Non-DOS Systems Only), and then to input a string of up to 240 characters, execute CALL 1BB3H. The input string will be in consecutive memory locations starting at the address contained in 40A7H-40A8H, with a zero byte at the end. The HL register pair will contain an address one less than the starting address of the stored input.
1BC0-1C8F - TOKENIZE INPUT ROUTINE
All "reserved" words are translated into single bytes with the MSB on. This saves space and time by allowing for table dispatch during execution. Therefore all statements appear together in the reserved word list in the same order they appear in in STMDSP.
NOTE: 40B0H holds the temporary storage location.
NOTE: Bit 0 HIGH means inside a quote. Bit 1 HIGH means inside a DATA. Bit 2 HIGH means inside a REM.
NOTE: In the original Model III ROM, this byte was placed into 40B0H which was just a temporary storage location.
NOTE: 40A7H-40A8H holds Input Buffer pointer.
NOTE: 40B0H holds the temporary storage location.
NOTE: Bit 0 HIGH means inside a quote. Bit 1 HIGH means inside a DATA. Bit 2 HIGH means inside a REM.
NOTE: In the original Model III ROM, this byte was read from 40B0H which was just a temporary storage location.
NOTE: Results from a CP:
- Z: A and * are the same
- NZ: A and * are NOT the same
- C: A < *
- NC: A => *
NOTE: Results from a CP:
- Z: A and * are the same
- NZ: A and * are NOT the same
- C: A < *
- NC: A => *
NOTE: Results from a CP:
- Z: A and * are the same
- NZ: A and * are NOT the same
- C: A < *
- NC: A => *
NOTE:
- The RST 10H routine loads the next character from the string pointed to by the HL register into the A-register and clears the CARRY FLAG if it is alphabetic, or sets it if is alphanumeric.
- Blanks and control codes 09H and 0BH are ignored causing the following character to be loaded and tested.
- The HL register will be incremented before loading any character therfore on the first call the HL register should contain the string address minus one.
- The string must be terminated by a byte of zeros.
- If A=61H it sets the ZERO FLAG
- If A<61H then the CARRY FLAG will be set
- if A>=61H then the NO CARRY FLAG will be set.
NOTE: 40B0H holds the temporary storage location.
NOTE: Bit 0 HIGH means inside a quote. Bit 1 HIGH means inside a DATA. Bit 2 HIGH means inside a REM.
NOTE: In the original Model III ROM, this byte was placed into 40B0H which was just a temporary storage location.
Still Inside the TOKENIZE INPUT ROUTINE - This routine is called if the character in register A is an end of the input character - "CRDONE".
NOTE: 40A7H-40A8H holds the pointer to the start of the Input Buffer.
1C90-1C95 - RST 0018H CODE
The RST 18H code is located here. (Unsigned compare (HL-DE)) - "DCOMPR".
This is the COMPARE DE:HL routine, which numerically compares DE and HL. Will not work for signed integers (except positive ones). Uses the A-register only. The result of the comparison is returned in the status register as:
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
1C96-1CA0 - RST 0008H CODE
The RST 8H code is located here.
This is the COMPARE SYMBOL routine which comparess the symbol in the input string pointed to by HL register to the value in the location following the RST 08 call. If there is a match, control is returned to address of the RST 08 instruction 2 with the next symbol in the Aregister and HL incremented by one. If the two characters do not match, a syntax error message is given and control returns to the Input Phase).
1CA1-1D1D - Level II BASIC "FOR" ROUTINE
NOTE: 40DCH holds FOR flag.
NOTE: 40E8H-40E9H holds Stack pointer pointer.
NOTE: 40A2H-40A3H holds the current BASIC line number.
NOTE: The RST 20H routine determines the type of the current value in REG 1 and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH).
The results are returned as follows:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
PUSH DE
NOTE: The RST 20H routine determines the type of the current value in REG 1 and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH).
The results are returned as follows:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
NOTE: 40DFH-40E0H holds Used by DOS.
1D1E-1D77 - LEVEL II BASIC INTERPRETER - New Statement Fetcher
NOTE: 40E6H-40E7H holds the temporary storage location.
NOTE: 40E8H-40E9H holds Stack pointer pointer.
NOTE: 40A2H-40A3H holds the current BASIC line number.
Honor a TRON by showing the line number if it is in effect.
NOTE: 411BH holds the TRON/TROFF flag.
Done with the TRON, so let us continue
NOTE:
- The RST 10H routine loads the next character from the string pointed to by the HL register into the A-register and clears the CARRY FLAG if it is alphabetic, or sets it if is alphanumeric.
- Blanks and control codes 09H and 0BH are ignored causing the following character to be loaded and tested.
- The HL register will be incremented before loading any character therfore on the first call the HL register should contain the string address minus one.
- The string must be terminated by a byte of zeros.
1D78-1D90 - RST 0010H CODE
The RST 10H code is located here. This is the EXAMINE NEXT SYMBOL routine which loads the next character from the string pointed to by the HL register set into the A-register and clears the CARRY FLAG if it is alphabetic, or sets it if is alphanumeric. Blanks and control codes 09 and OB are ignored causing the following character to be loaded and tested. The HL register will be incremented before loading any character therfore on the first call the HL register should contain the string address minus one. The string must be terminated by a byte of zeros) - "CHRGTR".
- If A=: it sets the ZERO FLAG
- If A<: then the CARRY FLAG will be set
- if A>=: then the NO CARRY FLAG will be set.
- If A=0BH it sets the ZERO FLAG
- If A<0BH then the CARRY FLAG will be set
- if A>=0BH then the NO CARRY FLAG will be set.
- If A=09H it sets the ZERO FLAG
- If A<09H then the CARRY FLAG will be set
- if A>=09H then the NO CARRY FLAG will be set.
- If A=0 it sets the ZERO FLAG
- If A<0 then the CARRY FLAG will be set
- if A>=0 then the NO CARRY FLAG will be set.
1D91-1D9A - LEVEL II BASIC "RESTORE" ROUTINE
- Note: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST).
NOTE: 40FFH-4100H holds READ pointer.
1D9B-1DAD - SCAN KEYBOARD ROUTINE
1DA0H - Inside the SCAN KEYBOARD ROUTINE - This will process a SHIFT + @ (Pause) Keypress
NOTE: 4099H holds the Last key pressed.
1DA9H-1DADH - Inside the SCAN KEYBOARD ROUTINE - This will process a "STOP" - "STOP".
This is the STOP entry point.
1DAE-1DE3 - LEVEL II BASIC "END" ROUTINE
NOTE: 40E6H-40E7H holds the temporary storage location.
NOTE: 40B5H-40D2H holds Temporary string work area.
NOTE: 40B3H-40B4H holds Next available location in the temporary string work area pointer.
NOTE: 40A2H-40A3H holds the current BASIC line number.
NOTE: 40F5H-40F6H holds the last line number executed.
NOTE: 40E6H-40E7H holds the temporary storage location.
NOTE: 40F7H-40F8H holds the last byte executed.
1DE4-1DF6 - LEVEL II BASIC "CONT" ROUTINE - "CONT".
NOTE: 40F7H-40F8H holds the last byte executed.
OR L
NOTE: 40F5H-40F6H holds the last line number executed.
NOTE: 40A2H-40A3H holds the current BASIC line number.
1DF7H-1DFCH - TRON and TROFF Routines
1DF7H - TRON Entry Point (Set A with AFH)
1DF8H - TROFF Entry Point (Set A with 00H)
1DF9-1DFC - COMMON CODE SHARED BY TRON AND TROFF - Put A into (411BH)
NOTE: 411BH holds the TRON/TROFF flag. 0=off (TROFF), 175=on (TRON)
1DFD-1DFF - DISK ROUTINE NOT USED BY LEVEL II BASIC - "POPAHT".
1E00H-1E3CH - "DEFxxx" Routine
1E00-1E02 - INSIDE THE DEFxxx ROUTINE - DEFSTR Entry Point
1E03H-1E05H - INSIDE THE DEFxxx ROUTINE - DEFINT Entry Point
1E06H-1E08H - INSIDE THE DEFxxx ROUTINE - DEFSNG Entry Point
1E09-1E0AH - INSIDE THE DEFxxx ROUTINE - DEFDBL Entry Point
1E0BH-1E3CH - INSIDE THE DEFxxx ROUTINE - Common code shared by all the above - All of those can either have a - for a range of values or be separated by ,. This code needs to figure out the variables that followed the DEF??? instruction and then set the variable type (which is currently sitting in Register E) in the variable table.
NOTE:
- The RST 10H routine loads the next character from the string pointed to by the HL register into the A-register and clears the CARRY FLAG if it is alphabetic, or sets it if is alphanumeric.
- Blanks and control codes 09H and 0BH are ignored causing the following character to be loaded and tested.
- The HL register will be incremented before loading any character therfore on the first call the HL register should contain the string address minus one.
- The string must be terminated by a byte of zeros.
NOTE:
- The RST 10H routine loads the next character from the string pointed to by the HL register into the A-register and clears the CARRY FLAG if it is alphabetic, or sets it if is alphanumeric.
- Blanks and control codes 09H and 0BH are ignored causing the following character to be loaded and tested.
- The HL register will be incremented before loading any character therfore on the first call the HL register should contain the string address minus one.
- The string must be terminated by a byte of zeros.
NOTE:
- The RST 10H routine loads the next character from the string pointed to by the HL register into the A-register and clears the CARRY FLAG if it is alphabetic, or sets it if is alphanumeric.
- Blanks and control codes 09H and 0BH are ignored causing the following character to be loaded and tested.
- The HL register will be incremented before loading any character therfore on the first call the HL register should contain the string address minus one.
- The string must be terminated by a byte of zeros.
NOTE: 4101H-411AH holds Variable Declaration Table.
NOTE:
- The RST 10H routine loads the next character from the string pointed to by the HL register into the A-register and clears the CARRY FLAG if it is alphabetic, or sets it if is alphanumeric.
- Blanks and control codes 09H and 0BH are ignored causing the following character to be loaded and tested.
- The HL register will be incremented before loading any character therfore on the first call the HL register should contain the string address minus one.
- The string must be terminated by a byte of zeros.
1E3D-1E44 - EXAMINE VARIABLE - See if the value pointed to at the memory location (HL) is an ASCII letter. C Flag is set for yes, NC Flag for no.
- If A=A it sets the ZERO FLAG
- If A<A then the CARRY FLAG will be set
- If A>=A then the NO CARRY FLAG will be set
- If A=Z it sets the ZERO FLAG
- If A<Z then the CARRY FLAG will be set
- If A>=Z then the NO CARRY FLAG will be set
1E45-1E4E - EXAMINE VARIABLE
This is called when evaluating a subscript for a variable reference. It will evaluate if the value is positive or negative.
NOTE:
- The RST 10H routine loads the next character from the string pointed to by the HL register into the A-register and clears the CARRY FLAG if it is alphabetic, or sets it if is alphanumeric.
- Blanks and control codes 09H and 0BH are ignored causing the following character to be loaded and tested.
- The HL register will be incremented before loading any character therfore on the first call the HL register should contain the string address minus one.
- The string must be terminated by a byte of zeros.
1E4A - FC ERROR entry point
1E4F-1E79 - ASCII TO BINARY
- If A=. it sets the ZERO FLAG
- If A<. then the CARRY FLAG will be set
- If A>=. then the NO CARRY FLAG will be set
NOTE: 40ECH-40EDH holds EDIT line number.
1E5A - READS A LINE # FROM THE CURRENT TEXT POSITION - "LINGET".
LINE NUMBERS RANGE FROM 0 TO 65529. THE ANSWER IS RETURNED IN [D,E]. [H,L] IS UPDATED TO POINT TO THE TERMINATING CHARACTER AND [A] CONTAINS THE TERMINATING CHARACTER WITH CONDITION CODES SET UP TO REFLECT ITS VALUE.
NOTE:
- The RST 10H routine loads the next character from the string pointed to by the HL register into the A-register and clears the CARRY FLAG if it is alphabetic, or sets it if is alphanumeric.
- Blanks and control codes 09H and 0BH are ignored causing the following character to be loaded and tested.
- The HL register will be incremented before loading any character therfore on the first call the HL register should contain the string address minus one.
- The string must be terminated by a byte of zeros.
Why 6552? Well, since the Z-80 has no multiply function, checking for any possible arbitrary number would require 4 branches for each step in the 'add to itself until it hits * 10' plus another 4 when adding the last digit. The TRS-80 ROM didn't have that kind of room, nor the time to do all that, so they needed a cheat and that cheat was to let it go as high 65529. After all, 6552 + 1 more digit can NEVER exceed 65535, but 6553 + 1 digit sure can!
So with this trick, the ROM just needs to first check the number against 6552, which, when multiplied by 10, and adding one more digit, will never exceed 65529 (because 9 is the highest one number can go).
By using this trick, only 1 comparison is needed (is it greater or less than 6552) ... at the cost of 4 usable line numbers/memory size setting in the 6553x range.
Wait, you say. 65535-65529 is 6 numbers, so why do you say 4. Well, another shortcut the ROM uses is that it assumes anything at line number 65535 is being entered in direct mode (i.e., no line number; just a command), so 65535 couldn't be a line number. Similarly, in 1A09, 65534 is reserved to trigger the BASIC interpreter to jump to the initial powerup routine in the ROM (i.e., a reboot) so you couldn't use that line number either.
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
This is so we can multiply HL (which should hold the number 6552) by 10.
This is so we can multiply HL (which should hold the number 6552) by 10.
As noted above, adding in any digit can only result in HL going to 65529.
1E7A-1EA0 - LEVEL II BASIC "CLEAR" ROUTINE
If Z FLAG is set, no number of bytes was provided (i.e., CLEAR instead of CLEAR 20).
NOTE:
- The RST 10H routine loads the next character from the string pointed to by the HL register into the A-register and clears the CARRY FLAG if it is alphabetic, or sets it if is alphanumeric.
- Blanks and control codes 09H and 0BH are ignored causing the following character to be loaded and tested.
- The HL register will be incremented before loading any character therfore on the first call the HL register should contain the string address minus one.
- The string must be terminated by a byte of zeros.
NOTE: 40B1H-40B2H holds MEMORY SIZE? pointer.
- Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
Note: 40A0H-40A1H holds the start of string space pointer.
1EA3-1EB0 - LEVEL II BASIC "RUN" ROUTINE
If Z FLAG is set, no line number was provided (i.e., RUN instead of RUN nnnn).
If we are here, then the command was RUN nnnn and we are in Cassette BASIC.
1EB1-1EC1 - LEVEL II BASIC "GOSUB" Command
Can be used to execute the equivalent of a GOSUB statement from an assembly program. It allows a BASIC subroutine to be called from an assembly subroutine. After the BASIC subroutine executes, control returns to the next statement in the assembly program. All registers are used. On entry, the HL must contain an ASCII string with the starting line number of the subroutine.
NOTE: 40A2H-40A3H holds the current BASIC line number.
1EC2-1EDD - LEVEL II BASIC "GOTO" ROUTINE - "GOTO".
Register pair DE should hold the nnnn for the GOTO nnnn command.
NOTE: The routine at 1E5A converts the ASCII string pointed to by HL to an integer deposited into DE. If the routine finds a non-numerica character, the conversion is stopped.
NOTE: 40A2H-40A3H holds the current BASIC line number.
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
Inside the GOTO ROUTINE - If we are here, the line number nnnn was not found, so error out.
1EDE-1E04 - LEVEL II BASIC "RETURN" ROUTINE
Returns control to the BASIC statement following the last GOSUB call. An assembly program called by a BASIC subroutine may wish to return directly to the orginal caller without returning through the subroutine entry point. This exit can be used for that return. The return address to the stack for the call to the assembly program must be cleared before returning via 1EDFH.
NOTE: 40E8H-40E9H holds Stack pointer pointer.
1EEC - "?RG ERROR" entry point.
NOTE: 40A2H-40A3H holds the current BASIC line number.
OR L
NOTE: 40DDH holds INPUT flag.
1F05-1F20 - SCAN ROUTINE
This is also the DATA entry point.
When an "if" takes a false branch it must find the appropriate "else" to start execution at. "data" counts the number of "if"s it sees so that the "else" code can match "else"s with "if"s. The count is kept in [d]
1F21-1F6B - LEVEL II BASIC "LET" ROUTINE
Without strings there is no need to watch quotations so [b] is set up as the secondary terminator and a scan is made for [b] or zero remember "GOSUB" falls into "data"
NOTE: 40DFH-40E0H holds Used by DOS.
NOTE: The RST 20H routine determines the type of the current value in REG 1 and returns a combination of STATUS flags and unique numeric values in the A Register according to the data mode flag (40AFH).
The results are returned as follows:
| If the Variable Type is ... | and the Flags are set ... | ... then Register A will be set ... |
| Integer | NZ/C/M/E | -1 |
| String | Z/C/P/E | 0 |
| Single Precision | NZ/C/P/O | 1 |
| Double Precision | NZ/NC/P/E | 5 |
- Note: 40A4H-40A5H holds the starting address of BASIC program text also known as the PROGRAM STATEMENT TABLE (PST).
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
Note: 40A0H-40A1H holds the start of string space pointer.
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
- Note: 40F9H-40FAH holds the starting address of the simple variable storage area.
| Condition | Flag |
| If HL < DE | CARRY SET |
| If HL > DE | NO CARRY |
| If HL ≠ DE | NZ |
| If HL = DE | Z |
LD A,D1H
1F6C-1FAE - LEVEL II BASIC ERROR "ON" ROUTINE
NOTE:
- The RST 10H routine loads the next character from the string pointed to by the HL register into the A-register and clears the CARRY FLAG if it is alphabetic, or sets it if is alphanumeric.
- Blanks and control codes 09H and 0BH are ignored causing the following character to be loaded and tested.
- The HL register will be incremented before loading any character therfore on the first call the HL register should contain the string address minus one.
- The string must be terminated by a byte of zeros.
NOTE: The routine at 1E5A converts the ASCII string pointed to by HL to an integer deposited into DE. If the routine finds a non-numerica character, the conversion is stopped.
OR E
LD E,C
NOTE: 40F0H-40F1H is used by ON ERROR.
NOTE: 40F2H holds the error flag.
NOTE: 409AH holds the RESUME flag.
1F95 - Still in the ON routine, but we know it isn't ON ERROR. We now need to deal with the possibility that it was an ON n GOTO or ON n GOSUB.
1FAF-1FF3 - LEVEL II BASIC "RESUME" ROUTINE
NOTE: 40F2H holds the error flag.
NOTE: 409AH holds the RESUME flag.
NOTE: The routine at 1E5A converts the ASCII string pointed to by HL to an integer deposited into DE. If the routine finds a non-numerica character, the conversion is stopped.
OR E
NOTE:
- The RST 10H routine loads the next character from the string pointed to by the HL register into the A-register and clears the CARRY FLAG if it is alphabetic, or sets it if is alphanumeric.
- Blanks and control codes 09H and 0BH are ignored causing the following character to be loaded and tested.
- The HL register will be incremented before loading any character therfore on the first call the HL register should contain the string address minus one.
- The string must be terminated by a byte of zeros.
1FCF - This is the "RESUME 0" routine
NOTE: 40EEH-40EFH is used by RESUME.
NOTE: 40EAH-40EBH holds the line number with error.
NOTE: 40A2H-40A3H holds the current BASIC line number.
NOTE: 40DDH holds INPUT flag.
1FF4H-2007 - LEVEL II BASIC ERROR ROUTINE - Evaluates n for "ERROR n"
- If A=2DH it sets the ZERO FLAG
- If A<2DH then the CARRY FLAG will be set
- if A>=2DH then the NO CARRY FLAG will be set.